/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#include "coin2/exchange/base/order/control/control_handler.h"

#include <glog/logging.h>
#include <nlohmann/json.hpp>

#include "naviasync/appinfo/build_info.h"

namespace coin2::exchange::base::order {

void ControlHandler::Menu::AddOpt(std::pair<std::string, std::string> opt, Task task) {
  opts_.emplace(std::make_pair(opt.first, opt.second));
  func_.emplace(std::make_pair(opt.first, std::move(task)));
}

bool ControlHandler::Menu::Process(std::string key) {
  auto iter = func_.find(key);
  if (iter != func_.end()) {
    iter->second();
    return true;
  }
  return false;
}

std::string ControlHandler::Menu::Menu::HelpDoc() {
  std::string help;
  help += "Help Doc.\n";
  help += "OPTIONS: \n";
  for (auto pair : opts_) {
    if (pair.first == "help") {
      continue;
    }
    help += pair.first + " -------- " + pair.second + '\n';
  }
  return help;
}

ControlHandler::ControlHandler(boost::asio::io_context* ioc, Callback* callback)
    : ioc_(ioc), callback_(callback), tcp_server_(new TcpServer(ioc, 0, this)) {
  Init();
}

ControlHandler::~ControlHandler() {}

void ControlHandler::Init() {
  // Callback Func
  auto enable_debug_log = [this]() { callback_->OnEvent(ControlType::EnableRawLog); };
  auto disable_debug_log = [this]() { callback_->OnEvent(ControlType::DisableRawLog); };
  auto help_doc = [this]() {
    auto help = menu_.HelpDoc();
    auto tcp_conn = tcp_conn_.lock();
    if (tcp_conn) {
      tcp_conn->SendMsg(help);
    }
  };
  auto build_info = [this]() {
    nlohmann::json json;
    auto build_info = GetBuildInfo();

    json["stable_builder"] = build_info.build_user();
    json["stable_machine"] = build_info.build_machine();
    json["stable_revision"] = build_info.revision();
    json["volatile_build_time"] = build_info.build_time_human_readable();
    auto tcp_conn = tcp_conn_.lock();
    if (tcp_conn) {
      tcp_conn->SendMsg(json.dump(2) + "\n");
    }
  };
  menu_.AddOpt(std::make_pair("enable_raw_log", "Enable raw log"), enable_debug_log);
  menu_.AddOpt(std::make_pair("disable_raw_log", "Disable raw log"), disable_debug_log);
  menu_.AddOpt(std::make_pair("help", "help doc"), help_doc);
  menu_.AddOpt(std::make_pair("build_info", "build info"), build_info);

  tcp_server_->RunAsync();
}

void ControlHandler::OnConnect(std::weak_ptr<TcpConnection> conn) {
  auto ptr = conn.lock();
  if (ptr) {
    ptr->RegisterCallback(this);
    tcp_conn_ = std::move(conn);
  }
}

void ControlHandler::OnRawDataReceived(
    std::weak_ptr<TcpConnection> conn,
    char* buff,
    uint32_t len) {
  auto ptr = conn.lock();
  if (ptr) {
    ProcessLine(buff, len);
  }
}

void ControlHandler::ProcessLine(char* buff, uint32_t len) {
  uint32_t i = 0;
  while (i < len) {
    if (buff[i] == '\n' || buff[i] == '\r') {
      Parse();
      line_.clear();
    } else {
      line_.push_back(buff[i]);
    }
    ++i;
  }
}

void ControlHandler::Parse() {
  DLOG(INFO) << "Parse: " << line_;
  std::string echo_str;
  if (menu_.Process(line_)) {
    echo_str += line_ + " success!\n";
  } else {
    echo_str += line_ + " fail!\n";
  }
  auto tcp_conn = tcp_conn_.lock();
  if (tcp_conn) {
    tcp_conn->SendMsg(echo_str);
  }
}

}  // namespace coin2::exchange::base::order
